package com.lzy.wzy.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.lzy.wzy.mapper.UserMapper;
import com.lzy.wzy.token.MyUsernamePasswordToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;

import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.token.TokenStore;

import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;

@Component
@Slf4j
public class AuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisTokenStore redisTokenStore;


    @Autowired
    private TokenStore jwtTokenStoreRe;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String requestUrl = request.getRequestURI();
        AntPathMatcher pathMatcher = new AntPathMatcher();
        if (pathMatcher.match("/", requestUrl)) {
            response.sendRedirect("/index/none/toLogin");
        }
        //1 oauth服务所有放行
        if (
                pathMatcher.match("/api/doLogin", requestUrl)
                        || pathMatcher.match("/oauth/**",requestUrl)
                        || pathMatcher.match("/logout", requestUrl)
                        || pathMatcher.match("/index/none/**", requestUrl)
                        || pathMatcher.match("/index/page/**", requestUrl)
                        || pathMatcher.match("/js/**", requestUrl)
                        || pathMatcher.match("/css/**", requestUrl)
                        || pathMatcher.match("/image/**", requestUrl)
                        || pathMatcher.match("/favicon.ico", requestUrl)
                        || pathMatcher.match("/dist/**", requestUrl)
//                        || pathMatcher.match("/webjars/**", requestUrl)
//                        || pathMatcher.match("/swagger-resources/**", requestUrl)
//                        || pathMatcher.match("/v2/**", requestUrl)

        ) {
            filterChain.doFilter(request, response);
            return;
        }

        //2 检查token是否存在
        String token = getToken(request);
        if (StringUtils.isBlank(token)) {
            noTokenMono(response);
            return;
        }
        OAuth2AccessToken oAuth2AccessToken;
        try {
            oAuth2AccessToken = jwtTokenStoreRe.readAccessToken(token);
            if (oAuth2AccessToken.isExpired()) {
                log.info("token 已过期");
                String refresh = request.getHeader("refresh");
                OAuth2RefreshToken oAuth2RefreshToken = jwtTokenStoreRe.readRefreshToken(refresh);
                System.out.println(jwtTokenStoreRe.readAccessToken(oAuth2RefreshToken.getValue()));
                expiredTokenMono(response);
                return;
            }
            Map<String, Object> additionalInformation = oAuth2AccessToken.getAdditionalInformation();
            String userUuid = String.valueOf(additionalInformation.get("userUuid"));
            String username = String.valueOf(additionalInformation.get("username"));
            List<String> list = JSON.parseObject(JSON.toJSONString(additionalInformation.get("lzy")), new TypeReference<ArrayList<String>>() {
            });
            if (requestUrl.split("/").length > 2) {
                requestUrl = requestUrl.substring(0,requestUrl.lastIndexOf("/")+1)+"**";
            }
            MyUsernamePasswordToken myUsernamePasswordToken = new MyUsernamePasswordToken(userUuid, username, AuthorityUtils.createAuthorityList(list.toArray(new String[0])), requestUrl);
            myUsernamePasswordToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(myUsernamePasswordToken);
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            invalidTokenMono(response);
        }
    }

    private String getToken(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        if (StringUtils.isBlank(token)) {
            return null;
        }
        token = token.substring(7);
        return token;
    }

    private void invalidTokenMono(HttpServletResponse response) throws IOException {
        JSONObject json = new JSONObject();
        json.put("status", HttpStatus.UNAUTHORIZED.value());
        json.put("msg", "无效的token");
        buildReturnMono(json, response);
    }

    private void expiredTokenMono(HttpServletResponse response) throws IOException {
        JSONObject json = new JSONObject();
        json.put("status", HttpStatus.UNAUTHORIZED.value());
        json.put("msg", "token已过期");
        buildReturnMono(json, response);
    }

    private void noTokenMono(HttpServletResponse response) throws IOException {
        JSONObject json = new JSONObject();
        json.put("status", HttpStatus.UNAUTHORIZED.value());
        json.put("msg", "没有token");
        buildReturnMono(json, response);
    }


    private void buildReturnMono(JSONObject json, HttpServletResponse response) throws IOException {
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json; charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.write(objectMapper.writeValueAsString(json));
        writer.flush();
    }
}
